package main

import (
	"fmt"
	"math/rand"
	"time"
)

// 使用select+channel实现最简单的多路复用
/**
假设要从网上下载一个文件，我启动了 3 个 goroutine 进行下载，并把结果发送到 3 个 channel 中。其中，哪个先下载好，就会使用哪个 channel 的结果。
在这种情况下，如果我们尝试获取第一个 channel 的结果，程序就会被阻塞，无法获取剩下两个 channel 的结果，也无法判断哪个先下载好。
这个时候就需要用到多路复用操作了，在 Go 语言中，通过 select 语句可以实现多路复用，其语句格式如下：
select {
case i1 = <-c1:
     //
case c2 <- i2:
	//
default:
	// default
}
整体结构和 switch 非常像，都有 case 和 default，只不过 select 的 case 是一个个可以操作的 channel。
小提示：多路复用可以简单地理解为，N 个 channel 中，任意一个 channel 有数据产生，select 都可以监听到，然后执行相应的分支，接收数据并处理。
*/
func downLoadFile(cName string) string {
	// 模拟下载文件
	n := rand.Int31n(3)
	fmt.Println(cName+",下载耗时：", n, "秒")
	time.Sleep(time.Duration(n) * time.Second)
	return cName + ":filePath"
}

func main() {
	c1 := make(chan string)
	c2 := make(chan string)
	c3 := make(chan string)

	go func() {
		fmt.Println("协程1运行")
		c1 <- downLoadFile("c1")
	}()
	go func() {
		fmt.Println("协程2运行")
		c1 <- downLoadFile("c2")
	}()
	go func() {
		fmt.Println("协程3运行")
		c1 <- downLoadFile("c3")
	}()

	/*
		如果这些 case 中有一个可以执行，select 语句会选择该 case 执行，
		如果同时有多个 case 可以被执行，则随机选择一个，这样每个 case 都有平等的被执行的机会。如果一个 select 没有任何 case，那么它会一直等待下去。
	*/
	select {
	case c1_out := <-c1:
		fmt.Println(c1_out)
	case c2_out := <-c2:
		fmt.Println(c2_out)
	case c3_out := <-c3:
		fmt.Println(c3_out)
	}

	time.Sleep(time.Second * time.Duration(5))
	fmt.Println("主协程运行完毕")
}
